﻿#pragma once
#include "yzrutil.h"
#include "List.hpp"
#include "lang/Exception.h"
#include "lang/System.h"
#include "util/Util.h"
#include "array/Array.hpp"
#include "array/Arrays.h"
#include <initializer_list>
namespace yzrilyzr_collection{
	template<typename E>
	ECLASS(ArrayList, public List<E>){
	private:
	static constexpr int DEFAULT_CAPACITY=10;
	yzrilyzr_array::Array<E> elementData;
	u_index size1=0;

	void grow(u_index minCapacity){
		this->modCount++;
		u_index oldCapacity=elementData.length;
		u_index newCapacity=yzrilyzr_array::Arrays::newLength(oldCapacity,
															  minCapacity - oldCapacity,
															  oldCapacity >> 1);
		elementData=yzrilyzr_array::Arrays::copyOf(elementData, newCapacity);
	}

	void grow(){
		grow(size1 + 1);
	}

	u_index indexOfRange(E o, u_index start, u_index end) const{
		for(u_index i=start; i < end; i++){
			if(o == elementData[i]){
				return i;
			}
		}
		return -1;
	}
	public:
	ArrayList() : ArrayList(DEFAULT_CAPACITY){}

	~ArrayList(){}

	explicit ArrayList(u_index size){
		this->elementData=yzrilyzr_array::Array<E>(size);
		this->size1=0;
	}

	explicit ArrayList(const std::initializer_list<E> &init) :ArrayList(init.begin(), init.size()){}

	explicit ArrayList(const yzrilyzr_array::Array<E> &array) :ArrayList(array._array, array.length){}

	explicit ArrayList(const E * array, u_index length) : size1(length) {
		elementData=yzrilyzr_array::Array<E>(length);
		memcpy(elementData._array, array, length * sizeof(E));
	}

		E & operator[](u_index index) const override{
		yzrilyzr_util::Util::checkIndex(index, size1);
		return elementData[index];
	}

	bool add(const E & e) override{
		this->modCount++;
		if(size1 == elementData.length)grow();
		elementData[size1]=e;
		size1=size1 + 1;
		return true;
	}

	void add(u_index index, const E & element) override{
		yzrilyzr_util::Util::checkIndex(index, size1);
		this->modCount++;
		u_index s=size1;
		if(s == elementData.length)grow();
		yzrilyzr_lang::System::arraycopy(elementData, index, elementData, index + 1, s - index);
		elementData[index]=element;
		size1=s + 1;
	}

	bool addAll(const Collection<E> & c) override{
		yzrilyzr_array::Array<E> a=c.toArray();
		this->modCount++;
		u_index numNew=a.length;
		if(numNew == 0)
			return false;
		u_index s=size1;
		if(numNew > elementData.length - s)grow(s + numNew);
		yzrilyzr_lang::System::arraycopy(a, 0, elementData, s, numNew);
		size1=s + numNew;
		return true;
	}
	E set(u_index index, const E & e) override{
		yzrilyzr_util::Util::checkIndex(index, size1);
		E old=elementData[index];
		elementData[index]=e;
		return old;
	}
	void ensureCapacity(u_index minCapacity){
		if(minCapacity > elementData.length && minCapacity <= DEFAULT_CAPACITY){
			this->modCount++;
			grow(minCapacity);
		}
	}
	E remove(u_index index) override{
		yzrilyzr_util::Util::checkIndex(index, size1);
		this->modCount++;
		E old=elementData[index];
		for(u_index i=index; i < size1 - 1; i++){
			E ele1=elementData[i + 1];
			elementData[i]=ele1;
		}
		size1=size1 - 1;
		return old;
	}

	bool remove(const E & o) override{
		this->modCount++;
		u_index size1=this->size();
		u_index i=0;
		for(; i < size1; i++)
			if(o == elementData[i])
				goto found;
		return false;
	found:
		remove(i);
		return true;
	}

	u_index size() const override{
		return size1;
	}

	E get(u_index i) const override{
		yzrilyzr_util::Util::checkIndex(i, size1);
		return elementData[i];
	}

	bool contains(const E & o) const override{
		return indexOf(o) >= 0;
	}

	u_index indexOf(const E & o) const override{
		return indexOfRange(o, 0, size1);
	}

	u_index lastIndexOf(const E & o) const override{
		return lastIndexOfRange(o, 0, size());
	}

	u_index lastIndexOfRange(const E & o, u_index start, u_index end) const{
		for(u_index i=end - 1; i >= start; i--){
			if(o == elementData[i]){
				return i;
			}
		}
		return -1;
	}

	void clear() override{
		this->modCount++;
		elementData=yzrilyzr_array::Array<E>(DEFAULT_CAPACITY);
		size1=0;
	}

	void trimToSize(){
		this->modCount++;
		elementData=yzrilyzr_array::Arrays::copyOf(elementData, size1);
	}
	void sort(yzrilyzr_util::Comparator<E> c){
		u_index expectedModCount=this->modCount;
		yzrilyzr_array::Arrays::sort(elementData, 0, size1, c);
		if(this->modCount != expectedModCount)
			throw yzrilyzr_lang::ConcurrentModificationException();
		this->modCount++;
	}
	yzrilyzr_array::Array<E> toArray(){
		return yzrilyzr_array::Arrays::copyOf(elementData, size1);
	}
	ECLASS(ArrayListItr, public Iterator<E>){
	protected:
	u_index cursor;       // index of next element to return
	u_index lastRet=-1; // index of last element returned; -1 if no such
	u_index expectedModCount;
	const ArrayList * clist=nullptr;
	ArrayList * list=nullptr;

	void checkForComodification(){
		if(clist->modCount != expectedModCount)
			throw yzrilyzr_lang::ConcurrentModificationException();
	}

	public:
	ArrayListItr(const ArrayList * clist, ArrayList * list) : clist(clist), list(list), expectedModCount(clist->modCount), cursor(0){}

	public:
	bool hasNext()const override{
		return cursor != clist->size();
	}

	E & next()override{
		checkForComodification();
		u_index i=cursor;
		if(i >= clist->size())
			throw yzrilyzr_lang::NoSuchElementException();
		if(i >= clist->elementData.length)
			throw yzrilyzr_lang::ConcurrentModificationException();
		cursor=i + 1;
		lastRet=i;
		return clist->elementData[lastRet];
	}
	void remove() override{
		if(lastRet < 0)
			throw yzrilyzr_lang::IllegalStateException();
		checkForComodification();
		list->remove(lastRet);
		cursor=lastRet;
		lastRet=-1;
		expectedModCount=clist->modCount;
	}
	};
	std::unique_ptr<Iterator<E>> iterator() override{
		return std::make_unique<ArrayListItr>(this, this);
	}
	std::unique_ptr<Iterator<E>> constIterator()const override{
		return std::make_unique<ArrayListItr>(this, nullptr);
	}
	};
}